package com.katze.boot.applet.asn1.anlysis;

import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.buf.HexUtils;
import org.bouncycastle.asn1.*;
import org.bouncycastle.util.Strings;

import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 说  明：ASN.1文件解析
 * 作  者：zf.zeng
 * 日  期：2021-04-30 (星期五)
 **/
@SuppressWarnings("unchecked")
public class ASN1Analysis extends AnalysisHelper {

    private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
    private static final char[] TBCD_SYMBOLS_CHARS = "0123456789*#abc".toCharArray();
    public static final String JOIN_CHAR = "-";
    private String delimiter = JOIN_CHAR;
    private boolean isVirtualNode = false;
    private Map<String, Map<String, String>> conf;

    public ASN1Analysis(){}

    public ASN1Analysis(boolean isVirtualNode, String delimiter){
        this.isVirtualNode = isVirtualNode;
        this.delimiter = delimiter;
    }

    public void storage(Path file){
        super.initDir(file);
    }

    public ASN1Analysis config(String filename) {
        if (StringUtils.isBlank(filename)) return this;
        try {
            this.conf = super.load(filename);
        } catch (IOException e) {
            throw new RuntimeException("配置文件：" + filename + "加载失败", e);
        }
        return this;
    }

    @Override
    protected Map<String, Object> read(Object data) throws IOException {
        Map<String, Object> dataSource = new LinkedHashMap<>() ;
        if(data instanceof byte[]){
            try (ASN1InputStream bIn = new ASN1InputStream((byte[])data)){
                ASN1Primitive primitive;
                while ((primitive = bIn.readObject()) != null) {
                    this.run(primitive, dataSource);
                }
            }
        } else {
            this.run(data, dataSource);
        }
        if (conf != null && conf.size() > 0) this.withFieldName(dataSource);
        return dataSource;
    }

    protected void run(Object data, Map<String, Object> treeSource) throws IOException {
        this.toTree("", data, treeSource);
    }

    protected void toTree(String tags, Object primitive, Map<String, Object> dataSource) throws IOException {
        if (primitive instanceof ASN1TaggedObject) {
            ASN1TaggedObject tagged = (ASN1TaggedObject) primitive;
            String tagsKey = delimiter.isEmpty() || tags.isEmpty() ? String.valueOf(tagged.getTagNo()):tags + delimiter + tagged.getTagNo();
            if ((tagged.getEncoded()[0] & BERTags.APPLICATION) == 64) {
                if ((tagged.getEncoded()[0] & BERTags.CONSTRUCTED) == 32) {
                    Map<String, Object> item = new LinkedHashMap<>();
                    toTree(tagsKey, tagged.getBaseUniversal(false, BERTags.SET), item);
                    dataSource.merge(tagsKey, item, (v1, v2)->{
                        if(v1 instanceof Map){
                            return Stream.of(v1, v2).collect(Collectors.toCollection(LinkedList::new));
                        } else {
                            ((LinkedList<Object>) v1).add(item);
                            return v1;
                        }
                    });
                } else {
                    byte[] encoded = tagged.toASN1Primitive().getEncoded();
                    if (tagged.isExplicit()) {
                        toTree(tagsKey, encoded, dataSource);
                    } else {
                        ByteArrayInputStream stream = new ByteArrayInputStream(encoded);
                        this.readTagNumber(stream, stream.read());
                        int length = this.readLength(stream, stream.available(), false);
                        int available = stream.available();
                        int size = length < 0 ? available - 2 : available;
                        if (size < 0) {
                            throw new ASN1ParsingException("failed to get contents");
                        } else {
                            byte[] contents = new byte[size];
                            System.arraycopy(encoded, encoded.length - available, contents, 0, size);
                            toTree(tagsKey, contents, dataSource);
                        }
                    }
                }
            } else if((tagged.getEncoded()[0] & BERTags.CONSTRUCTED) == 32){
                int tagValue = universal(tagged.getEncoded());
                int no;
                if((no = tagValue & BERTags.IA5_STRING) == 22 || (no = tagValue & BERTags.ENUMERATED) == 10 || (no = tagValue & BERTags.NUMERIC_STRING) == 18
                        || (no = tagValue & BERTags.UTF8_STRING) == 12){
                    toTree(isVirtualNode? tagsKey + delimiter + no:tagsKey , tagged.getBaseObject(), dataSource);
                } else if((no = tagValue & BERTags.SEQUENCE) == 16){
                    Map<String, Object> item = new LinkedHashMap<>();
                    toTree(isVirtualNode? tagsKey + delimiter + no:tagsKey, tagged.getBaseObject(), item);
                    List<Map<String, Object>> sequences = (List<Map<String, Object>>) dataSource.computeIfAbsent(tagsKey, (k) -> new LinkedList<>());
                    Object o = item.get(tagsKey);
                    if(o instanceof List){
                        sequences.addAll((List<Map<String, Object>>) o);
                    } else {
                        sequences.add(item);
                    }
                }else {
                    Map<String, Object> map = (Map<String, Object>) dataSource.computeIfAbsent(tagsKey, (k) -> new LinkedHashMap<>());
                    toTree(tagsKey, tagged.getBaseObject(), map);
                }
            } else {
                toTree(tagsKey, tagged.getBaseObject(), dataSource);
            }
        } else if (primitive instanceof ASN1Sequence) {
            Enumeration<?> enumeration = ((ASN1Sequence) primitive).getObjects();
            while (enumeration.hasMoreElements()) {
                Object next = enumeration.nextElement();
                if(next instanceof ASN1Sequence){
                    Map<String, Object> item = new LinkedHashMap<>();
                    toTree(tags, next, item);
                    ((List<Map<String, Object>>) dataSource.computeIfAbsent(tags, (key) ->  new LinkedList<>())).add(item);
                } else {
                    toTree(tags, next, dataSource);
                }
            }
        } else if(primitive instanceof ASN1Set){
            Enumeration<?> enumeration = ((ASN1Set)primitive).getObjects();
            while (enumeration.hasMoreElements()) toTree(tags, enumeration.nextElement(), dataSource);
        } else {
            if (primitive instanceof ASN1OctetString) {
                Map<String, String> field = conf != null && conf.containsKey(tags)? conf.get(tags):null;
               dataSource.merge(tags, this.toValue(field == null? "":field.get("type"), ((ASN1OctetString) primitive).getOctets()), this::mergeValue);
            } else if (primitive instanceof DERIA5String) {
                dataSource.merge(tags, ((DERIA5String) primitive).getString(), this::mergeValue);
            } else if (primitive instanceof ASN1Enumerated) {
                dataSource.merge(tags, ((ASN1Enumerated) primitive).getValue().toString(), this::mergeValue);
            } else if (primitive instanceof DERUTF8String) {
                dataSource.merge(tags, ((DERUTF8String) primitive).getString(), this::mergeValue);
            } else if(primitive instanceof ASN1Integer){
                dataSource.merge(tags, ((ASN1Integer) primitive).getValue().toString(), this::mergeValue);
            } else if (primitive instanceof DERGraphicString) {
                dataSource.merge(tags, ((DERGraphicString) primitive).getString(), this::mergeValue);
            } else if (primitive instanceof byte[]) {
                Map<String, String> field = conf != null && conf.containsKey(tags)? conf.get(tags):null;
                dataSource.merge(tags, this.toValue(field == null? "":field.get("type"), (byte[]) primitive), this::mergeValue);
            }
        }
    }

    protected int universal(byte[] data) {
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
        int num = stream.read() & 0x1f;
        if (num == 0x1f){
            do{
                num = stream.read();
            }while ((num >= 0) && ((num & 0x80) != 0));
        }
        num = stream.read();
        int size = (num > 127? num & 0x7f:0) + 1;
        for (int i = 0; i < size; i++) num = stream.read();
        return num;
    }

    private int readTagNumber(InputStream var0, int var1) throws IOException {
        int var2 = var1 & 31;
        if (var2 == 31) {
            int var3 = var0.read();
            if (var3 < 31) {
                if (var3 < 0) {
                    throw new EOFException("EOF found inside tag value.");
                }

                throw new IOException("corrupted stream - high tag number < 31 found");
            }

            var2 = var3 & 127;
            if (0 == var2) {
                throw new IOException("corrupted stream - invalid high tag number found");
            }

            while((var3 & 128) != 0) {
                if (var2 >>> 24 != 0) {
                    throw new IOException("Tag number more than 31 bits");
                }

                var2 <<= 7;
                var3 = var0.read();
                if (var3 < 0) {
                    throw new EOFException("EOF found inside tag value.");
                }

                var2 |= var3 & 127;
            }
        }

        return var2;
    }

    private int readLength(InputStream var0, int var1, boolean var2) throws IOException {
        int var3 = var0.read();
        if (0 == var3 >>> 7) {
            return var3;
        } else if (128 == var3) {
            return -1;
        } else if (var3 < 0) {
            throw new EOFException("EOF found when length expected");
        } else if (255 == var3) {
            throw new IOException("invalid long form definite-length 0xFF");
        } else {
            int var4 = var3 & 127;
            int var5 = 0;
            var3 = 0;

            do {
                int var6 = var0.read();
                if (var6 < 0) {
                    throw new EOFException("EOF found reading length");
                }

                if (var3 >>> 23 != 0) {
                    throw new IOException("long form definite-length more than 31 bits");
                }

                var3 = (var3 << 8) + var6;
                ++var5;
            } while(var5 < var4);

            if (var3 >= var1 && !var2) {
                throw new IOException("corrupted stream - out of bounds length found: " + var3 + " >= " + var1);
            } else {
                return var3;
            }
        }
    }

    protected Object mergeValue(Object v1, Object v2){
        if (v1 instanceof List){
            ((List<Object>) v1).add(v2);
            return v1;
        } else {
            return Stream.of(v1, v2).collect(Collectors.toCollection(LinkedList::new));
        }
    }

    protected String toValue(String type, byte[] data) {
        String value;
        StringBuilder builder = new StringBuilder();
        switch (type.toLowerCase()){
            case "int":
            case "integer": value = new BigInteger(data).toString(); break;
            case "long": value = Long.valueOf(toShortHexString(data), 16).toString(); break;
            case "boolean": value = data[0] == 0? ASN1Boolean.FALSE.toString():ASN1Boolean.TRUE.toString(); break;
            case "string": value = new DEROctetString(data).toString(); break;
            case "ia5string": value = Strings.fromByteArray(data); break;
            case "octetstring": value = toShortHexString(data); break;
            case "utf8string": value = Strings.fromUTF8ByteArray(data); break;
            case "ascii": value = new String(data); break;
            case "datetime":
                try {
                    value = ASN1UTCTime.getInstance(data).getDate().toLocaleString();
                } catch (ParseException e) {
                    value = toShortHexString(data);
                }
                break;
            case "ipv4":
            case "ipv4string":
                for (int i = 0; i < data.length; i++) {
                    builder.append(data[i] & 0xFF);
                    if (i < data.length - 1) builder.append(".");
                }
                value = builder.toString();
                break;
            case "ipv6":
            case "ipv6string":
                value = toShortHexString(data);
                for (int i = 0; i < value.length(); i += 4) {
                    builder.append(value, i, i + 4);
                    if (i < value.length() - 4) builder.append(":");
                }
                value = builder.toString();
                break;
            case "graphicstring":
                value = new DERGraphicString(data).getString();
                break;
            default: value = toBusinessValue(type, data); break;
        }
        return value;
    }

    protected String toBusinessValue(String type, byte[] bytes){
        StringBuilder builder = new StringBuilder();
        switch (type){
            case "STRING4G":
                StringBuilder buffer2 = new StringBuilder();
                byte[] s = new byte[1];
                System.arraycopy(bytes, 0, s, 0, s.length);
                String first1 = toTBCD(s);
                buffer2.append(first1);

                s = new byte[1];
                System.arraycopy(bytes, 1, s, 0, s.length);
                String se1 = toShortHexString(s);
                String se11 = se1.substring(0, 1);
                String se12 = se1.substring(1);
                buffer2.append(se12);

                s = new byte[1];
                System.arraycopy(bytes, 2, s, 0, s.length);
                String th1 = toTBCD(s);
                buffer2.append(th1);
                buffer2.append(se11);

                if (buffer2.charAt(buffer2.length() - 1) == 'F'){
                    return buffer2.deleteCharAt(buffer2.length() - 1).toString();
                } else {
                    return buffer2.toString();
                }

            case "userLocationInformation":
            case "UserLocationInformation":
                byte[] b = new byte[1];
                System.arraycopy(bytes, 2, b, 0, b.length);
                long zxType = binaryToString(b);
                String sexType = toShortHexString(b);
                builder.append(sexType);
                if (137 == zxType) {
                    b = new byte[1];
                    System.arraycopy(bytes, 3, b, 0, b.length);
                    String thrid = toTBCD(b);
                    builder.append(thrid);
                    b = new byte[1];
                    System.arraycopy(bytes, 4, b, 0, b.length);
                    String fore = toShortHexString(b);
                    String f1 = fore.substring(0, 1);
                    String f2 = fore.substring(1);
                    builder.append(f2);

                    b = new byte[1];
                    System.arraycopy(bytes, 5, b, 0, b.length);
                    String five = toTBCD(b);
                    builder.append(five);
                    b = new byte[3];
                    System.arraycopy(bytes, 6, b, 0, b.length);
                    String six = toShortHexString(b);
                    builder.append(six);

                    b = new byte[1];
                    System.arraycopy(bytes, 9, b, 0, b.length);
                    String se = toTBCD(b);
                    builder.append(se);
                    b = new byte[1];
                    System.arraycopy(bytes, 10, b, 0, b.length);
                    String ei = toShortHexString(b);
                    String ei1 = ei.substring(0, 1);
                    String ei2 = ei.substring(1);
                    builder.append(ei2);

                    b = new byte[1];
                    System.arraycopy(bytes, 11, b, 0, b.length);
                    String ni = toTBCD(b);
                    builder.append(ni);
                    b = new byte[5];
                    System.arraycopy(bytes, 12, b, 0, b.length);
                    String ten = toShortHexString(b);
                    builder.append(ten);
                } else if (130 == zxType) {
                    b = new byte[1];
                    System.arraycopy(bytes, 3, b, 0, b.length);
                    String thrid = toTBCD(b);
                    builder.append(thrid);

                    b = new byte[1];
                    System.arraycopy(bytes, 4, b, 0, b.length);
                    String fore = toShortHexString(b);
                    String f2 = fore.substring(1);
                    builder.append(f2);

                    b = new byte[1];
                    System.arraycopy(bytes, 5, b, 0, b.length);
                    String five = toTBCD(b);
                    builder.append(five);

                    b = new byte[2];
                    System.arraycopy(bytes, 6, b, 0, b.length);
                    String six = toShortHexString(b);
                    builder.append(six);

                    b = new byte[1];
                    System.arraycopy(bytes, 8, b, 0, b.length);
                    String serv = toTBCD(b);
                    builder.append(serv);

                    b = new byte[1];
                    System.arraycopy(bytes, 9, b, 0, b.length);
                    String nine = toShortHexString(b);
                    String n2 = nine.substring(1);
                    builder.append(n2);

                    b = new byte[1];
                    System.arraycopy(bytes, 10, b, 0, b.length);
                    String ten = toTBCD(b);
                    builder.append(ten);

                    b = new byte[bytes.length - 11];
                    System.arraycopy(bytes, 11, b, 0, b.length);
                    String el = toShortHexString(b);
                    builder.append(el);

                } else {
                    b = new byte[bytes.length - 3];
                    System.arraycopy(bytes, 3, b, 0, b.length);
                    String another = toShortHexString(b);
                    builder.append(another);
                }
                return builder.toString();
            case "userLocationInformationLTE45Format":
            case "UserLocationInformationLTE45Format":
                return toLTEUserLocationInformation(bytes, 45);
            case "userLocationInformationLTE08Format":
            case "UserLocationInformationLTE08Format":
                return toLTEUserLocationInformation(bytes, 8);
            case "userLocationInformationLTE00Format":
            case "UserLocationInformationLTE00Format":
                return toLTEUserLocationInformation(bytes, 0);
            case "DATE_MSCE_YYYYMD":
                return toDateYMD(bytes, 2, 1, 1);
            case "DATE_MSCE_HMS":
                return toDateYMD(bytes, 1, 1, 1);
            case "DATE_MSCE_HMSTOSEC":
                return dateToSec(bytes, 1, 1, 1);
            case "TBCD_ADDRESS_STRING":
                return toTBCDAddressDlydj(bytes);
            case "TBCD_CALLNBR_STRING"://lwy-dlydj-210327
                return toTBCDCallNbrDlydj(bytes);
            case "OCTET_MSCID_STRING"://lwy-dlydj-210327
                return getMscID(bytes);
            case "IMEI_TYPE_ORDER"://lwy-dlydj-210327
                byte[] b1 = new byte[1];
                System.arraycopy(bytes, 0, b1, 0, b1.length);
                byte[] b2 = new byte[bytes.length - b1.length];
                System.arraycopy(bytes, b1.length, b2, 0, b2.length);
                return String.format(
                        "%03d%08d",
                        Integer.parseInt(HexUtils.toHexString(b1), 16),
                        Integer.parseInt(HexUtils.toHexString(b2), 16));
            case "VOLTE_NBR"://lwy-VOLET时长话单-210406
                return getVoltenbr(bytes);
            case "ZTECN_NULL"://lwy-DLYDJ-如果字段的长度有值,就返回1,否则返回0
                return bytes.length >= 0 ? "1" : "0";
            case "ASN1_NULL"://lwy-DLYDJ-如果字段的长度有值,就返回1,否则返回0
                return bytes.length >= 0 ? String.valueOf(Long.parseLong(toShortHexString(bytes), 16)) : "";
            case "ASN1_INVOLVEDPARTY"://lwy-DLYDJ-如果字段的长度有值,就返回1,否则返回0
                return funcHwAsn1InvolvedParty(bytes);
            case "ASN1_DXVPNCALLNUMBER"://lwy-欣方智能网先去86再替换"+"为00
                return funcDXVPNCallNumber(bytes);
            case "ASN1_CHREPLACE":
                return new String(bytes).replaceAll("\\*","b").replaceAll("#","c");
            case "LTECG_PLMN":
                List<String> vs = Stream.of(toTBCD(bytes, false).split("")).collect(Collectors.toList());
                if (vs.size() == 6){
                    type = vs.remove(3);
                    return  (String.join("", vs) + type).replace("F", "");
                } else {
                    return toShortHexString(bytes);
                }
            case "TBCDOneString":
                return toTBCD(bytes, true);
            default: return toShortHexString(bytes);
        }
    }

    public static String toShortHexString(byte[] bytes) {
        int len = bytes.length * 2;
        char[] hexChars = new char[len];

        for(int j = 0; j < bytes.length; ++j) {
            int v = bytes[j] & 255;
            hexChars[j * 2] = HEX_CHARS[v >>> 4];
            hexChars[j * 2 + 1] = HEX_CHARS[v & 15];
        }
        return new String(hexChars);
    }

    private String toTBCD (byte[] tbcd) {//高低位转换
        int size = (tbcd == null ? 0 : tbcd.length);
        StringBuffer buffer = new StringBuffer(2*size);
        for (int i=0; i<size; ++i) {
            int octet = tbcd[i];
            int n2 = (octet >> 4) & 0xF;
            int n1 = octet & 0xF;

            if (n1 == 15) {
                throw new NumberFormatException("Illegal filler in octet n=" + i);
            }
            buffer.append(TBCD_SYMBOLS_CHARS[n1]);

            if (n2 == 15) {
                if (i != size-1)
                    throw new NumberFormatException("Illegal filler in octet n=" + i);
            } else
                buffer.append(TBCD_SYMBOLS_CHARS[n2]);
        }

        return buffer.toString();
    }

    /**
     * 依据TBCDUtil.toTBCD改造
     *
     * @param rmf 是否去掉F填充字符
     */
    private String toTBCD(byte[] data, boolean rmf) {
        StringBuilder buffer = new StringBuilder();
        if (data != null) {
            for (byte datum : data) {
                int n2 = (datum >> 4) & 0xF;
                int n1 = datum & 0xF;

                if (n1 == 15) {
                    if (!rmf) buffer.append(HEX_CHARS[n1]);
                } else {
                    buffer.append(HEX_CHARS[n1]);
                }

                if (n2 == 15) {
                    if (!rmf) buffer.append(HEX_CHARS[n2]);
                } else {
                    buffer.append(HEX_CHARS[n2]);
                }
            }
        }
        return buffer.toString();
    }

    private long binaryToString(byte[] bytes) {
        long num = 0;
        int count = 0;
        for (int i = bytes.length - 1; i >= 0; i--) {
            num += (bytes[count] & 0xFF) * (Math.pow(256, i));
            count++;
        }
        return num;
    }

    private String toLTEUserLocationInformation(byte[] data, int format) {
        int index = 0; //下标
        String binaryString = StringUtils.leftPad(Integer.toBinaryString(data[index] & 0xff), 8, '0'); //首字节转二进制，为1的需要解析
        String[] types = binaryString.split("");
        index++;//第一个字节跳过
        byte[] lteBytes;
        String[] values = new String[2];
        String taiValue = "", ecgiValue = "";
        // CGI
        if (types[7].equals("1")) index += 7;
        // SAI
        if (types[6].equals("1")) index += 7;
        // RAI
        if (types[5].equals("1")) index += 7;

        //TAI
        if (types[4].equals("1") && index <= data.length) {
            lteBytes = new byte[3];
            System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
            taiValue += toTBCD(lteBytes, false);
            index += lteBytes.length;

            lteBytes = new byte[2];
            System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
            taiValue += toShortHexString(lteBytes);
            index += lteBytes.length;
            values[1] = taiValue;
        }

        //ECGI
        if (types[3].equals("1") && index <= data.length) {
            lteBytes = new byte[3];
            System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
            ecgiValue += toTBCD(lteBytes, false);
            index += lteBytes.length;

            lteBytes = new byte[1];
            System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
            ecgiValue += Integer.toHexString(lteBytes[0] & 0xFF).toUpperCase();//占位符
            index += lteBytes.length;

            lteBytes = new byte[3];
            if (data.length < 13) {
                System.arraycopy(data, index, lteBytes, 0, data.length - index);
            } else {
                System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
            }
            ecgiValue += toShortHexString(lteBytes);
            values[0] = ecgiValue;
        }
        if (format == 45) {
            return ecgiValue;
        } else if (format == 8) {
            if (ecgiValue.equals("") && taiValue.equals("")) {
                return "";
            } else {
                return ecgiValue + "," + taiValue;
            }
        } else if (format == 0) {
            return taiValue;
        } else {
            return String.join(",", values);
        }
    }

    /**
     * |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
     * MSB	                                            octet 1（年）
     * LSB	octet 2（年）
     * octet 3（月）
     * octet 4（日）
     * 说明：
     * 年（octet 1 ~ octet 2）：取值范围0 ~ 9999 （H’0 – H’270F）
     * 月（octet 2）：取值范围1 ~ 12 （H’0 – H’0C    删除3B）
     * 日（octet 3）：取值范围1 ~ 31 （H’0 – H’1F    删除3B）
     */
    private String toDateYMD(byte[] data, Integer... position) {
        int index = 0;
        StringBuilder dateValue = new StringBuilder();
        for (Integer item : position) {
            byte[] bytes = new byte[item];
            System.arraycopy(data, index, bytes, 0, bytes.length);
            index += item;
            int value = new ASN1Integer(bytes).getValue().intValue();
            if (value < 10) {
                dateValue.append("0").append(value);
            } else {
                dateValue.append(value);
            }
        }
        return dateValue.toString();
    }


    /**
     * DLYSJ业务-主叫号码专用类型-跳过第一个字符并进行高低位转换
     *
     * @param data
     */
    private String toTBCDAddressDlydj(byte[] data) {
        int index = 0; //下标
        index++;//第一个字节跳过
        //TAI
        byte[] lteBytes = new byte[data.length - 1];
        System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
        String taiValue = toTBCD(lteBytes, false);
        taiValue = taiValue.replace("F", "");//去F
        return taiValue;
    }

    private String dateToSec(byte[] data, Integer... position) {
        int index = 0;
        StringBuilder dateValue = new StringBuilder();
        for (Integer item : position) {
            byte[] bytes = new byte[item];
            System.arraycopy(data, index, bytes, 0, bytes.length);
            index += item;
            int value = new ASN1Integer(bytes).getValue().intValue();
            if (value < 10) {
                dateValue.append("0").append(value);
            } else {
                dateValue.append(value);
            }
        }
        if (dateValue.toString().length() == 6) {
            int sec1 = Integer.parseInt(dateValue.toString().substring(0, 2)) * 60 * 60;
            int sec2 = Integer.parseInt(dateValue.toString().substring(2, 4)) * 60;
            int sec3 = Integer.parseInt(dateValue.toString().substring(4, 6));
            return String.valueOf(sec1 + sec2 + sec3);
        } else if (dateValue.toString().length() == 4) {
            int sec1 = Integer.parseInt(dateValue.toString().substring(0, 2)) * 60;
            int sec2 = Integer.parseInt(dateValue.toString().substring(2, 4));
            return String.valueOf(sec1 + sec2);
        }
        return dateValue.toString();
    }

    private String toTBCDCallNbrDlydj(byte[] data) {
        int index = 0; //下标
        index++;//第一个字节跳过
        //TAI
        byte[] lteBytes = new byte[data.length - 1];
        System.arraycopy(data, index, lteBytes, 0, lteBytes.length);
        return toTBCD(lteBytes, false).replace("F", "");//去F
    }

    /**
     * mscid字段共3个字节.前2个字节转化为整形数据5位,不足前补0,后一字节转化为整形数据2位,不足前补0
     */
    private String getMscID(byte[] data) {
        byte[] bytes = null;
        String result;
        if (data.length >= 3) {
            bytes = new byte[2];
            System.arraycopy(data, 0, bytes, 0, bytes.length);
            String toRes = toShortHexString(bytes);
            String res = String.valueOf(Integer.parseInt(toRes, 16));
            int reslen = res.length();
            if (reslen < 5) {
                for (int i = 0; i < 5 - reslen; i++) {
                    res = "0" + res;
                }
            }
            bytes = new byte[1];
            System.arraycopy(data, 2, bytes, 0, bytes.length);
            String toStr = toShortHexString(bytes);
            String str = String.valueOf(Integer.parseInt(toStr, 16));
            int strlen = str.length();
            if (strlen < 2) {
                for (int i = 0; i < 2 - strlen; i++) {
                    str = "0" + str;
                }
            }
            result = res + str;
        } else {
            String toErr = toShortHexString(data);
            String err = String.valueOf(Integer.parseInt(toErr, 16));
            result = err;
        }
        return result;
    }

    private String getVoltenbr(byte[] data) {
        //String str = "sip:8617850844593.fj.ims.mnc000.ims8617829040109.mcc460.3gppnetwork.org8617346402340";
        //这个方法用于取出上面字符串中所有的电话号码并以8617850844593|8617829040109|8617346402340格式返回
        //String str = "sip:8617850844593.fj
        //若只有一个则不用加|
        String str = new String(data);
        if(StringUtils.isBlank(str)){
            return str;
        }
        str = str.replaceAll("&","");
        int numStrIndex = 0;
        int numEndIndex = 0;

        if (str.contains("sip:")){
            numStrIndex = str.indexOf("sip:")+4;
            if (str.contains("sip:+")) {
                numStrIndex = str.indexOf("sip:+")+5;
            }
        }
        if (str.contains("tel:")){
            numStrIndex = str.indexOf("tel:")+4;
            if (str.contains("tel:+")) {
                numStrIndex = str.indexOf("tel:+")+5;
            }
        }
        if (str.startsWith("+")){
            numStrIndex = 1;
        }
        numEndIndex = numStrIndex;
        for (int i = numStrIndex; i < str.length(); i++) {
            if (Character.isDigit(str.charAt(i))) {//判断是否是数字
                numEndIndex += 1;
            }else {
                break;//不是则停止
            }
        }
        String number = str.substring(numStrIndex, numEndIndex);
        return getServedMSISDN(number);//去86
    }

    /**
     * 函数名称:FuncDXVPNCallNumber
     * 函数功能:转换Bit字符串(高低位互换)的值
     * 输入参数:
     char *sFunctionName	功能函数名
     TParamList *ParamStrings	转化参数
     * 输出参数:格式化后的字符串
     * 返 回 值:!= NULL  读取成功,  NULL  读取失败
     **/
    private String funcDXVPNCallNumber(byte[] data) {
        String str = getServedMSISDN(new String(data));//去86
        if (str.startsWith("+")) {
            str = str.replace("+", "00");
        }
        return str;
    }

    private String funcHwAsn1InvolvedParty(byte[] data) {
        /*tag=0的数据样例("sip:+865923671212@ims.fj.ctcims.com")
	  tag=1的数据样例 "tel:+865923671212"或者" tel:+86-595-53271001"
	格式化处理流程:对tag=0的取@前的字符,然后过滤掉字段中的非数字字符,最后再按C网的号码截取策略格式化号码*/
        //修改内容:增加*pOrgCdr='*' || *pOrgCdr='#'这两种情况
        //修改原因:对于号码中含有'*','#'的情况，不能过滤
        String str = new String(data);
        String regEx = "[^0-9+*#]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
        return m.replaceAll("").trim();
    }

    private String getServedMSISDN(String phonenum) {//去国家码
        if (phonenum == null || "".equals(phonenum)) {
            return "";
        } else if (phonenum.startsWith("0086")) {
            phonenum = phonenum.substring(4);
        } else if ((phonenum.startsWith("+86") || phonenum.startsWith("086")) && phonenum.length() > 9) {
            phonenum = phonenum.substring(3);
        } else if (phonenum.startsWith("86") && phonenum.length() > 9) {
            phonenum = phonenum.substring(2);
        }
        return phonenum;
    }

    private void withFieldName(Map<String, Object> dataSource){
        for (Map.Entry<String, Object> entry : dataSource.entrySet()) {
            if (entry.getValue() instanceof Map){
                withFieldName((Map<String, Object>) entry.getValue());
            } else if (entry.getValue() instanceof List){
                for(Object item : (List<Object>) entry.getValue()){
                    if (item instanceof Map){
                        withFieldName((Map<String, Object>) item);
                    } else {
                        Map<String, String> field = conf.get(entry.getKey());
                        entry.setValue(field.get("name") + " ::= " + item.toString());
                    }
                }
            } else {
                Map<String, String> field = conf.get(entry.getKey());
                if (field != null){
                    entry.setValue(field.get("name") + " ::= " + entry.getValue());
                }
            }
        }
    }
}
